package com.cgs.query.server.connection

import com.cgs.query.connection.ConnectionFactory
import com.cgs.query.domain.QuerySource
import com.cgs.query.service.IQueryProjectService
import com.cgs.query.service.IQuerySourceService
import com.cgs.query.util.Utils
import com.codahale.metrics.MetricRegistry
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import mu.KotlinLogging
import org.hibernate.QueryException
import java.sql.Connection
import java.util.concurrent.ConcurrentHashMap
import javax.sql.DataSource

private val logger = KotlinLogging.logger { }

/**
 * 根据配置文件自动构建连接工厂
 */
class ServerPoolConnectionFactory(
        private val querySourceService: IQuerySourceService
) : ConnectionFactory {
    private var metricRegistry: MetricRegistry? = null
    private val projectName2SourceId = ConcurrentHashMap<String, String>()
    private val sourceId2DataSource = ConcurrentHashMap<String, HikariDataSource>()

    override fun getConnection(projectName: String): Connection {
        TODO()
        /*return sourceId2DataSource[projectName2SourceId.computeIfAbsent(projectName) {
            val source = queryProjectService.findSourceByName(projectName)
                    ?: throw QueryException("项目[$projectName]找不到数据源")
            sourceId2DataSource.computeIfAbsent(source.id) {
                source.hikariBuild()
            }
            source.id
        }]?.connection ?: throw QueryException("项目[$projectName]找不到数据源")*/
    }

    override fun getConnection(querySource: QuerySource): Connection {
        return sourceId2DataSource.computeIfAbsent(querySource.id) {
            if (querySource.isIdOnly()) {
                querySourceService.findById(it)?.hikariBuild()
                        ?: throw QueryException("找不到数据源:[${querySource.id}]")
            } else {
                querySource.hikariBuild()
            }
        }.connection
    }

    override fun returnConnection(conn: Connection) = conn.close()

    override fun removeConnectionBySourceId(sourceId: String) {
        projectName2SourceId.entries.filter { it.value == sourceId }.toList().forEach {
            projectName2SourceId.remove(it.key)
        }
        sourceId2DataSource.remove(sourceId)?.close()
    }

    override fun removeConnection(projectName: String) {
        projectName2SourceId.remove(projectName)
    }

    override fun getDataSource(sourceId: String): DataSource {
        return sourceId2DataSource.computeIfAbsent(sourceId) {
            querySourceService.findById(it)?.hikariBuild()
                    ?: throw QueryException("找不到数据源:[$sourceId]")
        }
    }

    override fun setMetricRegistry(MetricRegistry: MetricRegistry) {
        this.metricRegistry = MetricRegistry
    }

    fun QuerySource.hikariBuild(): HikariDataSource {

        return HikariDataSource(HikariConfig().also {
            it.poolName = this.id
            it.jdbcUrl = this.url
            it.username = this.userName
            it.password = this.password
            it.maximumPoolSize = this.maxPoolSize
            it.connectionTimeout = this.timeout
            it.idleTimeout = this.idleTimeout
            it.maxLifetime = this.maxLifetime
            Utils.notNullAndThen(metricRegistry) { mr ->
                it.metricRegistry = mr
            }
            logger.info { "Build HikariDataSource:[$name]" }
        })
    }
}

fun QuerySource.isIdOnly(): Boolean {
    return url.isBlank()
}

fun <R> millisTimeOutThenRun(timeOut: Long, block: () -> R, timeOutBlock: () -> Unit): R {
    val start = System.currentTimeMillis()
    val r = block()
    if ((System.currentTimeMillis() - start) > timeOut) {
        timeOutBlock()
    }
    return r
}

/*fun QuerySource.jdbcEquals(dataSource: HikariDataSource): Boolean {
    return this.url == dataSource.jdbcUrl && this.userName == dataSource.username
            && this.password == dataSource.password
}*/
